home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998…tember: Reference Library / Dev.CD Sep 98 RL1.toast / Technical Documentation / develop / develop Issue 22 / develop Issue 22 code / PCI Driver Sample.sea / PCI Driver Sample / NCR_DriverProject / Src / DriverPrepRequest.c / DriverPrepRequest.c
Encoding:
C/C++ Source or Header  |  1995-07-24  |  9.9 KB  |  246 lines  |  [TEXT/MPCC]

  1. /*                                    DriverPrepRequest.c                                */
  2. /*
  3.  * DriverPrepRequest.c
  4.  * Copyright © 1994-95 Apple Computer Inc. All rights reserved.
  5.  */
  6. /*    .___________________________________________________________________________________.
  7.       | These routines manage the details of calling PrepareMemoryForIO for a PBRead or    |
  8.       | or PBWrite request. The process is normally straightforward, but can become        |
  9.       | quite complex. The code flow for simple transfers is as follows:                    |
  10.       |        1. PBRead/PBWrite calls PrepareNewDMATransfer                                |
  11.       |        2. PrepareNewDMATransfer calls PrepareMemoryForIO and PrepareNextDMA        |
  12.       |        3. On return, the DMA parameters are sent to the hardware.                    |
  13.       | The more complex code paths handle the following problems:                        |
  14.       |    If the user area is discontiguous, PrepareNextDMA will be re-called from the    |
  15.       |        Primary Interrupt Service Routine to compute the next physical transfer        |
  16.       |        area and length. In many drivers this would be pre-processed into a            |
  17.       |        device-specific scatter-gather list.                                        |
  18.       |    If PrepareMemoryForIO did not prepare the entire area, it must be re-called        |
  19.       |        to handle partial preparation. In this case, the primary interrupt service    |
  20.       |        routine queues a Software Task and exits the interrupt without restarting    |
  21.       |        the device. When the Software Task runs, it calls PrepareMemoryForIO,        |
  22.       |        re-calls PrepareNextDMA, and queues a Secondary Interrupt handler that        |
  23.       |        restarts I/O. Error handling in this case is tricky, as an error must        |
  24.       |        cause the device to abort the transfer (otherwise subsequent transfers        |
  25.       |        won't be started. This is needed if the physical map is too small, if the    |
  26.       |        device granularity is smaller than the transfer length, or -- in some cases    |
  27.       |        if a virtual memory "page turn" forces partial preparation. Because of the    |
  28.       |        latter, this driver MUST NOT be in the virtual memory paging process.        |
  29.       |    The overall algorithm is given in the documentation of the DMA library.            |
  30.       | Functions:                                                                        |
  31.       |    PrepareNewDMATransfer    Called from driver mainline to initialize variables and    |
  32.       |                            prepare the first DMA sequence.                            |
  33.       |    PrepareNextDMATask        Software Task queued from the primary interrupt to        |
  34.       |                            continue preparation.                                    |
  35.       |    PrepareNextDMA            Called after preparation and I/O to compute the next    |
  36.       |                            DMA physical address and transfer length.                |
  37.       |    ConfigureThisPhysicalArea Performs the actual address/length computation.        |
  38.       | PrepareNextDMA returns a status value that controls the caller:                    |
  39.       |    noErr                Success: start (or restart) I/O                                |
  40.       |    kPrepareMemoryRestart After a primary interrupt: the function queued a Software    |
  41.       |                        Task to handle partial preparation.    The interrupt routine    |
  42.       |                        should exit without restarting I/O.    When the Software Task    |
  43.       |                        completes, it will queue our Secondary Interrupt handler    |
  44.       |                        with this status to continue the I/O operation.                |
  45.       |    scsiDataRunError    Failure: The I/O table "done" state was set. This means the    |
  46.       |                        device wants to transmit more data than the application.    |
  47.       |                        The interrupt routine should halt the transfer.                |
  48.       |    other error            Failure from PrepareMemoryForIO. The interrupt routine        |
  49.       |                        should halt the transfer.                                    |
  50.       | The Software Task queues the common Secondary Interrupt Handler with status set    |
  51.       | as follows:                                                                        |
  52.       |    kPrepareMemoryRestart Success after partial preparation: restart I/O            |
  53.       |                        If there is an error, the SCSI Script will eventually        |
  54.       |                        fail through its I/O rundown and return scsiDataRunError    |
  55.       |                        (The only error returned by PrepareMemoryForIO is paramErr    |
  56.       |                        which isn't too useful.)                                    |
  57.     .___________________________________________________________________________________.
  58. */
  59. #include "NCRDriverPrivate.h"
  60. /*
  61.  * The current request will always be referenced through this local variable.
  62.  */
  63. #define REQUEST    (*perRequestDataPtr)
  64. #define PB        (*((IOParam *) REQUEST.pb))
  65. #define SCSI    (*((NCRSCSIParamPtr) PB.ioMisc))
  66. #define IOTABLE    (REQUEST.scsiIOTable)
  67.  
  68. OSErr                        DoInitialPreparation(
  69.         PerRequestDataPtr        perRequestDataPtr
  70.     );
  71.     
  72. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  73.  * PrepareNewDMATransfer initializes a user I/O data transfer request. It initializes
  74.  * the dma parameters, calls PrepareMemoryForIO, and specifies the first DMA address
  75.  * and length. If this transfer does not support a data phase, it returns noErr without
  76.  * preparing anything.
  77.  */
  78. OSErr
  79. PrepareNewDMATransfer(
  80.         PerRequestDataPtr        perRequestDataPtr
  81.     )
  82. {
  83.         OSErr                    status;
  84.         IOPreparationOptions    options;
  85.  
  86.         Trace(PrepareNewDMATransfer);
  87.         CLEAR(IOTABLE);
  88.         IOTABLE.preparationID = kInvalidID;        /* Marker for CheckpointIOTable        */
  89.         switch (SCSI.driverAction) {
  90.         case kNCRDriverNoDataPhase:
  91.             status = noErr;
  92.             options = 0;
  93.             break;
  94.         case kNCRDriverInputAllowed:
  95.             options = kIOIsInput;
  96.             break;
  97.         case kNCRDriverOutputAllowed:
  98.             options = kIOIsOutput;
  99.             break;
  100.         default:
  101.             options = 0;
  102.             status = paramErr;
  103.             break;
  104.         }
  105.         if (options != 0) {                                /* Preparation required        */
  106.             PB.ioActCount = 0;                            /* Nothing transferred yet    */
  107.             IOTABLE.options =
  108.                     ( options                            /* Input or output            */
  109.                     | (0 * kIOMultipleRanges)            /* No scatter-gather list    */
  110.                     | (1 * kIOLogicalRanges)            /* Logical addresses        */
  111.                     | (0 * kIOMinimalLogicalMapping)    /* No output logical map    */
  112.                     | (1 * kIOShareMappingTables)        /* Share with Kernel        */
  113.                     | (0 * kIOCoherentDataPath)            /* No fancy data path        */
  114.                     );
  115.             IOTABLE.addressSpace = REQUEST.addressSpaceID;
  116.             IOTABLE.logicalMapping = NULL;                /* We don't do logical xfer    */
  117.             IOTABLE.rangeInfo.range.base = PB.ioBuffer;
  118.             IOTABLE.rangeInfo.range.length = PB.ioReqCount;
  119.             IOTABLE.granularity = kNCRDriverMaxTransfer;
  120.             IOTABLE.mappingEntryCount = REQUEST.scsiMapEntries;
  121.             IOTABLE.physicalMapping = REQUEST.physicalMapTables;
  122.             IOTABLE.state = 0;                            /* Clear done bit            */
  123.             IOTABLE.firstPrepared = 0;
  124.             Timestamp('PMI+');
  125.             status = PrepareMemoryForIO(&IOTABLE);
  126.             Timestamp('PMI-');
  127.             CheckStatus(status, "\pPrepareMemoryForIO initial");
  128.             /*
  129.              * The test for a non-zero length is redundant: PrepareMemoryForIO will
  130.              * return a paramErr if a zero-length ioReqCount is provided.
  131.              */
  132.             if (status == noErr && IOTABLE.lengthPrepared == 0)
  133.                 status = scsiDataRunError;
  134.             /*
  135.              * After preparing the data area, initialize our private information.
  136.              */
  137.             status = DoInitialPreparation(perRequestDataPtr);
  138.         }
  139.         return (status);
  140. }
  141.  
  142. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  143.  * PrepareNextDMATask is the SoftwareTask that is executed in order to start the next
  144.  * PrepareMemoryForIO request. The secondary interrupt routine previously checked the
  145.  * "done" bit.
  146.  */
  147. void
  148. PrepareNextDMATask(
  149.         void                    *p1,    /* perRequestDataPtr    */
  150.         void                    *p2        /* Unused                */
  151.     )
  152. {
  153.         OSErr                    status;
  154. #define perRequestDataPtr ((PerRequestDataPtr) p1)
  155.  
  156.         Timestamp('SSI-');
  157.         Trace(PrepareNextDMATask);
  158.         UNUSED(p2);
  159.         /*
  160.          * Partial preparation. This should be extended to handle errors from
  161.          * PrepareMemoryForIO. For example, we could stuff an error status into
  162.          * the high short word in the p2 parameter.
  163.          */
  164.         Timestamp('PaP+');
  165.         status = PrepareMemoryForIO(&REQUEST.scsiIOTable);
  166.         Timestamp('PaP-');
  167.         CheckStatus(status, "\pPrepareMemoryForIO partial prep");
  168.         if (status == noErr) {
  169.             status = DoInitialPreparation(perRequestDataPtr);
  170.             CheckStatus(status, "\pInitial(re)Preparation");
  171.         }
  172.         /*
  173.          * Restart I/O - we do this by calling our common Secondary Interrupt routine.
  174.          * The status value tells the Secondary Interrupt routine to restart the device..
  175.          */
  176.         (void) NCRQueueSecondaryInterrupt(perRequestDataPtr, kPrepareMemoryRestart);
  177. #undef perRequestDataPtr
  178. }
  179.  
  180.  
  181. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  182.  * DoInitialPreparation is called from the driver mainline to start a transfer, and
  183.  * from the software task to continue DMA transfers after a re-preparation.
  184.  */
  185. OSErr
  186. DoInitialPreparation(
  187.         PerRequestDataPtr        perRequestDataPtr
  188.     )
  189. {
  190.         OSErr                    status;
  191.  
  192.         Trace(DoInitialPreparation);
  193.         status = InitializeDMATransfer(&REQUEST.scsiIOTable, 0, &REQUEST.dmaTransferInfo);
  194.         CheckStatus(status, "\pInitializeDMATransfer");
  195.         return (status);
  196. }
  197.  
  198. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  199.  * PrepareNextDMA handles the details of a single DMA burst. Here, we assume that we
  200.  * cannot build a physical scatter-gather table, but must construct each DMA transfer
  201.  * by itself. It uses the following from the REQUEST record:
  202.  *    REQUEST.dmaTransferInfo        This will be prepared by the initial call
  203.  *                                and used by other calls.
  204.  *    REQUEST.thisDMATransfer        The result: length will be non-zero if successful.
  205.  * Return:
  206.  *    noErr                        Success -- start or continue the transfer.
  207.  *    scsiDataRunError            Failure - no more data can be transfered.
  208.  *    other errors                Failure.
  209.  * Return scsiDataRunError if we're at the end of the transfer, or noErr if we
  210.  * did the preparation. The Primary Interrupt routine will send a Software Interrupt,
  211.  * other states will fail.
  212.  */
  213. OSErr
  214. PrepareNextDMA(
  215.         PerRequestDataPtr        perRequestDataPtr
  216.     )
  217. {
  218.         OSErr                    status;
  219.         Boolean                    isLogicalTransfer;
  220.  
  221.         Trace(PrepareNextDMA);
  222.         status = PrepareDMATransfer(
  223.                     &REQUEST.dmaTransferInfo,
  224.                     &REQUEST.thisDMATransfer,
  225.                     &isLogicalTransfer
  226.                 );
  227.         if (status == noErr
  228.          && (isLogicalTransfer || REQUEST.thisDMATransfer.length == 0)) {
  229.              LogString("\pPrepareDMATransfer bogus return");
  230.              status = paramErr;
  231.          }
  232. #if 1 && USE_LOG_LIBRARY
  233.         WriteLogEntry(GLOBAL.logRecordPtr, 'pDMA',
  234.             LogFormat4(kLogFormatSigned, kLogFormatAddress,
  235.                 kLogFormatUnsigned, kLogFormatString),
  236.             (signed long) status,
  237.             REQUEST.thisDMATransfer.base,
  238.             REQUEST.thisDMATransfer.length,
  239.             "\pNextDMA"
  240.         );
  241. #endif
  242.         if (status != noErr)
  243.             REQUEST.thisDMATransfer.length = 0;
  244.         return (status);
  245. }
  246.